<?php
/**
 * Eka Framework 0.3.0
 *
 * @copyright Copyright (c) 2008 Alexey Yu. Chuprina <656b61@gmail.com> (http://php-eka.googlecode.com)
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 *
 * @version $Id$
 */

/**
 * Eka_Application_Request
 *
 * @package Application
 * @subpackage Request
 */
class Eka_Application_Request
{
	static protected function stripslashes($value)
	{
		return $value = is_array($value)
			? array_map(array('Eka_Application_Request', 'stripslashes'), $value)
			: stripslashes($value);
	}

################################################################################

	/**
	 * Path of request
	 *
	 * @var array
	 */
	protected $path;
	
	protected $realPath;

	/**
	 * Base Url
	 *
	 * @var string
	 */
	protected $baseUrl;

	/**
	 * Request data
	 *
	 * @var array
	 */
	protected
		$params = array(),
		$query,
		$post,
		$cookie,
		$files,
		$server,
		$env;
		
	/**
	 * @var array
	 */
	protected $filesCache = array();
		
	/**
	 * @param $params array
	 */
	public function __construct($data = null)
	{
		if (get_magic_quotes_gpc()) {
			$this->query = self::stripslashes($_GET);
			$this->post = self::stripslashes($_POST);
			$this->cookie = self::stripslashes($_COOKIE);
		} else {
			$this->query = $_GET;
			$this->post = $_POST;
			$this->cookie = $_COOKIE;
		}
		$this->files = $_FILES;
		$this->server = $_SERVER;
		$this->env = $_ENV;
		
		if (null !== $data) {
			if (isset($data['_GET'])) {
				$this->query = array_merge($this->query, $data['_GET']);
			}
			if (isset($data['_POST'])) {
				$this->post = array_merge($this->post, $data['_POST']);
			}
			if (isset($data['_COOKIE'])) {
				$this->cookie = array_merge($this->cookie, $data['_COOKIE']);
			}
			if (isset($data['_FILES'])) {
				$this->files = array_merge($this->files, $data['_FILES']);
			}
			if (isset($data['_SERVER'])) {
				$this->server = array_merge($this->server, $data['_SERVER']);
			}
			if (isset($data['_ENV'])) {
				$this->env = array_merge($this->env, $data['_ENV']);
			}
		}
		
		$url = '';
		if (isset($this->server['HTTP_HOST'])) {
			$protocol = empty($this->server['HTTPS']) ? 'http' : 'https';
			$port = $this->server['SERVER_PORT'];
			$url = $protocol . '://' . $this->server['HTTP_HOST'];
			if (('http' == $protocol && 80 != $port) || ('https' == $protocol && 443 != $port)) {
				$url .= ':' . $port;
			}
		}

		if (isset($this->server['HTTP_X_REWRITE_URL'])) { //IIS
			$url .= $this->server['HTTP_X_REWRITE_URL'];
		} elseif(isset($this->server['REQUEST_URI'])) {
			$url .= $this->server['REQUEST_URI'];
		} elseif (isset($this->server['ORIG_PATH_INFO'])) { //IIS 5.0, PHP as CGI
			$url .= $this->server['ORIG_PATH_INFO'];
		}

		$this->setUrl($url);
	}

	/**
	 * Set request URL
	 *
	 * @param string $url
	 */
	public function setUrl($url)
	{
		$url = parse_url($url);

		$this->baseUrl = null;
		if (isset($url['scheme']) && isset($url['host'])) {
			$this->baseUrl = $url['scheme'] . '://' . $url['host'];
			if (isset($url['port'])) {
				$this->baseUrl .= ':' . $url['port'];
			}
		}

		$this->path = array();
		if (isset($url['path'])) {
			foreach (explode('/', $url['path']) as $elm) {
				if ('' !== $elm) {
					$this->path[] = $elm;
				}
			}
		}
		if (isset($url['query'])) {
			$get = array();
			parse_str($url['query'], $get);
			$this->query = array_merge($this->query, $get);
		}
	}

	/**
	 * Return request URL
	 *
	 * @return string
	 */
	public function getUrl()
	{
		$query = '';
		if (!empty($this->query)) {
			$query = '?' . http_build_query($this->query);
		}
		return  $this->baseUrl . $this->getPath() . $query;
	}
	
	/**
	 * Return real request URL
	 *
	 * @return string
	 */
	public function getRealUrl()
	{
		$url = '';
		if (isset($_SERVER['HTTP_HOST'])) {
			$protocol = empty($_SERVER['HTTPS']) ? 'http' : 'https';
			$port = $_SERVER['SERVER_PORT'];
			$url = $protocol . '://' . $_SERVER['HTTP_HOST'];
			if (('http' == $protocol && 80 != $port) || ('https' == $protocol && 443 != $port)) {
				$url .= ':' . $port;
			}
		}

		if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
			$url .= $_SERVER['HTTP_X_REWRITE_URL'];
		} elseif(isset($_SERVER['REQUEST_URI'])) {
			$url .= $_SERVER['REQUEST_URI'];
		} elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
			$url .= $_SERVER['ORIG_PATH_INFO'];
		}
		
		return $url;
	}

	/**
	 * Return request Base URL
	 *
	 * @return string
	 */
	public function getBaseUrl()
	{
		return $this->baseUrl;
	}

	/**
	 * Request path as string
	 *
	 * @param int $offset
	 * @return string
	 */
	public function getPath($offset = 0)
	{
		$path = $this->path;
		if ($offset > 0) {
			$path = array_slice($path, $offset);
		}
		return '/' . join('/', $path);
	}
	
	/**
	 * Set request path
	 *
	 * @param string $path
	 * @param int $offset
	 */
	public function setPath($path, $offset = 0)
	{
		if ($offset > 0) {
			$path = join('/', array_slice($this->path, 0, $offset)) . '/' . $path;
		}
		
		$this->path = array();
		foreach (explode('/', $path) as $elm) {
			if ('' !== $elm) {
				$this->path[] = $elm;
			}
		}
	}

	/**
	 * Return path segment
	 *
	 * @param int $pos Segment position
	 * @return string Segment of request path
	 */
	public function getPathSegment($pos)
	{
		return isset($this->path[$pos]) ? $this->path[$pos] : null;
	}

	/**
	 * Return path segments as array
	 *
	 * @return array;
	 */
	public function getPathSegments($offset = 0, $length = null)
	{
		if ($offset > 0) {
			return array_slice($this->path, $offset, $length);
		}
		return $this->path;
	}
	
	/**
	 * Return formated path segment
	 *
	 * @param int $pos Segment position
	 * @return string Formated segment of request path
	 */
	public function getFormatedPathSegment($pos)
	{
		return $this->formatePathSegment($this->getPathSegment($pos));
	}
	
	/**
	 * Return path segments as array
	 *
	 * @return array;
	 */
	public function getFormatedPathSegments($offset = 0, $length = null)
	{
		return array_map(array($this, 'formatePathSegment'), $this->getPathSegments($offset, $length));
	}

	/**
	 * Return the method by which the request was made
	 *
	 * @return string
	 */
	public function getMethod() {
		return $this->getServer('REQUEST_METHOD');
	}

	/**
	 * Was the request made by POST?
	 *
	 * @return boolean
	 */
	public function isPost() {
		return ($this->getMethod() == 'POST');
	}

	/**
	 * Is the request a Javascript XMLHttpRequest?
	 *
	 * @return boolean
	 */
	public function isXmlHttpRequest() {
		return ($this->getHeader('X_REQUESTED_WITH') == 'XMLHttpRequest');
	}

	/**
	 * @return string
	 */
	public function getReferer()
	{
		return $this->getServer('HTTP_REFERER');
	}

	/**
	 * @param $name string
	 * @return bool
	 */
	public function hasParam($name)
	{
		return isset($this->params[$name]) || isset($this->post[$name]) || isset($this->query[$name]);
	}

	/**
	 * @param string $name
	 * @param mixed $default
	 * @return mixed
	 */
	public function getParam($name, $default = null)
	{
		switch (true) {
			case isset($this->params[$name]):
				return $this->params[$name];
			case isset($this->post[$name]):
				return $this->post[$name];
			case isset($this->query[$name]):
				return $this->query[$name];
			default:
				return $default;
		}
	}
	
	/**
	 * @param string $name
	 * @param mixed $value
	 */
	public function setParam($name, $value)
	{
		$this->params[$name] = $value;
	}
	
	/**
	 * @param array $params
	 */
	public function setParams($params)
	{
		$this->params = array_merge($this->params, $params);
	}

	/**
	 * @return array
	 */
	public function getParams()
	{
		return array_merge($this->params, $this->query, $this->post);
	}

	/**
	 * Return the value of the given HTTP header
	 *
	 * If no $name is passed, returns all HTTP headers.
	 *
	 * @param string $name HTTP header name
	 * @return string HTTP header value, or $default if not found
	 */
	public function getHeader($name = null, $default = null)
	{
		if (null === $name) {
			$headers = array();
			foreach ($this->server as $name => $value) {
				if (strpos($name, 'HTTP_') !== 0) continue;
				$name = substr($name, 5);
				$name = str_replace('_', ' ', strtolower($name));
				$name = str_replace(' ', '-', ucwords($name));
				$headers[$name] = $value;
			}
			return $headers;
		}

		$name = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
		return isset($this->server[$name]) ? $this->server[$name] : $default;
	}

	/**
	 * Return value of Get
	 *
	 * If no $name is passed, returns the entire GET array.
	 *
	 * @param string $name
	 * @param mixed $default Default value to use if key not found
	 * @return mixed Returns null if key does not exist
	 */
	public function getQuery($name = null, $default = null)
	{
		return $this->getHelper('query', $name, $default);
	}
	
	/**
	 * Return query string
	 *
	 * @return string
	 */
	public function getQueryString()
	{
		return $this->getServer('QUERY_STRING');
	}
	
	/**
	 * Return value of Post
	 *
	 * If no $name is passed, returns the entire Post array.
	 *
	 * @param string $name
	 * @param mixed $default Default value to use if key not found
	 * @return mixed Returns null if key does not exist
	 */
	public function getPost($name = null, $default = null)
	{
		return $this->getHelper('post', $name, $default);
	}
	
	/**
	 * Return value of Server
	 *
	 * If no $name is passed, returns the entire Server array.
	 *
	 * @param string $name
	 * @param mixed $default Default value to use if key not found
	 * @return mixed Returns null if key does not exist
	 */
	public function getServer($name = null, $default = null)
	{
		return $this->getHelper('server', $name, $default);
	}
	
	/**
	 * Return value of Env
	 *
	 * If no $name is passed, returns the entire Env array.
	 *
	 * @param string $name
	 * @param mixed $default Default value to use if key not found
	 * @return mixed Returns null if key does not exist
	 */
	public function getEnv($name = null, $default = null)
	{
		return $this->getHelper('env', $name, $default);
	}

	/**
	 * Return uploaded file
	 *
	 * @param string $name
	 * @return Eka_Application_Request_File
	 * @throws Eka_Application_Request_File_Exception
	 */
	public function getFile($name)
	{
		if (!isset($this->filesCache[$name])) {
			if (!isset($this->files[$name])) {
				include_once 'Eka/Application/Request/File/Exception.php';
				throw new Eka_Application_Request_File_Exception('The file was not loaded', UPLOAD_ERR_NO_FILE);
			}
			$error = (int)$this->files[$name]['error'];
			switch ($error) {
				case UPLOAD_ERR_OK:
					break;
				case UPLOAD_ERR_INI_SIZE:
					include_once 'Eka/Application/Request/File/Exception.php';
					throw new Eka_Application_Request_File_Exception('Size of uploaded file exceeded the maximum allowable size', $error);
				case UPLOAD_ERR_FORM_SIZE:
					include_once 'Eka/Application/Request/File/Exception.php';
					throw new Eka_Application_Request_File_Exception('The uploaded file exceeds the value MAX_FILE_SIZE, specified in the HTML-form', $error);
				case UPLOAD_ERR_PARTIAL:
					include_once 'Eka/Application/Request/File/Exception.php';
					throw new Eka_Application_Request_File_Exception('The downloadable file has been received only in part', $error);
				default:
					include_once 'Eka/Application/Request/File/Exception.php';
					throw new Eka_Application_Request_File_Exception("Undefined error ($error)", $error);
			}
			$this->filesCache[$name] = new Eka_Application_Request_File($this->files[$name]);
		}
		return $this->filesCache[$name];
	}
	
	/**
	 * @param $name string
	 * @return bool
	 */
	public function hasCookie($name)
	{
		return isset($this->cookie[$name]);
	}
	
	/**
	 * Return value of Cookie
	 *
	 * If no $name is passed, returns the entire Cookie array.
	 *
	 * @param string $name
	 * @param mixed $default Default value to use if key not found
	 * @return mixed Returns null if key does not exist
	 */
	public function getCookie($name = null, $default = null)
	{
		return $this->getHelper('cookie', $name, $default);
	}
	
	public function setQuery($name, $value)
	{
		$this->query[$name] = $value;
	}
	
	public function setPost($name, $value)
	{
		$this->post[$name] = $value;
	}
	
	public function setServer($name, $value)
	{
		$this->query[$name] = $value;
	}
	
	public function setEnv($name, $value)
	{
		$this->query[$name] = $value;
	}
	
	public function setCookie($name, $value)
	{
		$this->cookie[$name] = $value;
	}

	private function getHelper($var, $name, $default)
	{
		if (null === $name) {
			return $this->{$var};
		}
		return isset($this->{$var}[$name]) ? $this->{$var}[$name] : $default;
	}
	
	public function formatePathSegment($segment)
	{
		if (null === $segment) {
			return null;
		}
		/*if (preg_match('~[^a-z0-9\-_\.]~i', $segment)) {
			return null;
		}*/
		$segment = str_replace(array('_', '-', '.' ), ' ', strtolower($segment));
		$segment = str_replace(' ', '', ucwords($segment));
		return $segment;
	}
}